package v1ch14.future;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

/**
 * @version 1.01 2012-01-26
 * @author Cay Horstmann
 */
public class FutureTest
{
   public static void main(String[] args)
   {
      //Scanner in = new Scanner(System.in);

      //System.out.print("Enter base directory (e.g. /usr/local/jdk5.0/src): ");
     // String directory = in.nextLine();
      //System.out.print("Enter keyword (e.g. volatile): ");
      //String keyword = in.nextLine();
      String directory="D:\\temp\\jdk_src";
      String keyword="getStartPosition";

      MatchCounter counter = new MatchCounter(new File(directory), keyword);
      FutureTask<Integer> task = new FutureTask<>(counter);
      Thread t = new Thread(task);
      t.start();

      MatchFileList matchFileList=new MatchFileList(new File(directory),keyword);
      FutureTask<List<File>> task1=new FutureTask<List<File>>(matchFileList);
      Thread t1=new Thread(task1);
      t1.start();


      try
      {
         System.out.println(task.get() + " matching files.");
         System.out.println(" matching files: "+task1.get());
      }
      catch (ExecutionException e)
      {
         e.printStackTrace();
      }
      catch (InterruptedException e)
      {
      }
   }
}

/**
 * This task counts the files in a directory and its subdirectories that contain a given keyword.
 */
class MatchCounter implements Callable<Integer>
{
   private File directory;
   private String keyword;
   private int count;

   /**
    * Constructs a MatchCounter.
    * @param directory the directory in which to start the search
    * @param keyword the keyword to look for
    */
   public MatchCounter(File directory, String keyword)
   {
      this.directory = directory;
      this.keyword = keyword;
   }

   public Integer call()
   {
      count = 0;
      try
      {
         File[] files = directory.listFiles();
         List<Future<Integer>> results = new ArrayList<>();

         for (File file : files)
            if (file.isDirectory())
            {
               MatchCounter counter = new MatchCounter(file, keyword);
               FutureTask<Integer> task = new FutureTask<>(counter);
               results.add(task);
               Thread t = new Thread(task);
               t.start();
            }
            else
            {
               if (search(file,keyword)) count++;
            }

         for (Future<Integer> result : results)
            try
            {
               count += result.get();
            }
            catch (ExecutionException e)
            {
               e.printStackTrace();
            }
      }
      catch (InterruptedException e)
      {
      }
      return count;
   }

   /**
    * Searches a file for a given keyword.
    * @param file the file to search
    * @return true if the keyword is contained in the file
    */
   static  public boolean search(File file,String keyword)
   {
      try
      {
         try (Scanner in = new Scanner(file))
         {
            boolean found = false;
            while (!found && in.hasNextLine())
            {
               String line = in.nextLine();
               if (line.contains(keyword)) found = true;
            }
            return found;
         }
      }
      catch (IOException e)
      {
         return false;
      }
   }


}

class MatchFileList implements Callable<List<File>> {
   private File directory;
   private String keyword;
   private List<File> fileList=new ArrayList<>();

   public MatchFileList(File directory,String keyword){
      this.directory=directory;
      this.keyword=keyword;
   }

   @Override
   public List<File> call() throws Exception {
      List<Future<List<File>>> futureList=new ArrayList<>();
      File[] files=directory.listFiles();
      for(File f:files){
         //System.out.println("-Nsl-:In MatchFileList.call(): " + f.getCanonicalPath() + "(f.getCanonicalPath())");
         if(f.isDirectory()){
            MatchFileList matchFileList=new MatchFileList(f,keyword);
            FutureTask<List<File>> futureTask=new FutureTask<List<File>>(matchFileList);
            futureList.add(futureTask);
            new Thread(futureTask).start();
         }else {
            if(MatchCounter.search(f,keyword)){
               fileList.add(f);
            }
         }
      }

      for (Future<List<File>> listFuture : futureList) {
         fileList.addAll(listFuture.get());
      }

      return fileList;
   }
}
